/*
 * Copyright (c) 2018-2024 北京华恒盛世科技有限公司
 * QUICKPOOL is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
*/

#include "xdrbase.h"
#include "channel.h"
#include "hash.h"
#include "api.h"

struct quickpoolInfo *gptrMyInfo=NULL;

static int compare_node_name(void *, void *);

/**
 * @brief       API: get node list information from the cluster.
 *
 * @param[in]   nodereq      #1: query node list request key values
 * @param[in]   master       #2: master(port and name) to be called
 *
 * @retval      not NULL     #1: succeed
 * @retval      NULL         #2: failed
 *
 * @note        When succeed, QuickPoolHostReply should be freed by the caller.
 *              When failed, error message is set to reply in this API.
 */
struct QuickPoolHostReply *
client_get_hostinfo(struct request *nodereq, struct poolMaster *master)
{
    if (NULL == nodereq || 0 == nodereq->number) {
        log_message(ERR, NOCLASS, "%s: Must specify node request.", __func__);
	return NULL;
    }

    // check the first key to avoid unknown request from client.
    if (REQUEST_KEY_OPTIONS != nodereq->keyvalues[0].key) {
        log_message(ERR, NOCLASS, "%s: First key must be option for node request.",
                    __func__);
	return NULL;
    }

    struct auth auth;
    gf_set_auth(&auth, commandParams[AUTH_KIND].string_value);

    unsigned int size; // for calculating the size of the request.
    size = gf_xdrsize_request(nodereq);
    size += gf_xdrsize_auth(&auth);
    size += HEADER_LEN;

    char *request;
    request = (char *)calloc(size, sizeof(char));
    if (NULL == request) {

        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", size);
        gf_xdr_free(gf_xdr_auth, (void *)&auth);
        quickpool_errno = ERROR_MEMORY;
        return NULL;
    }

    XDR xdrs;
    xdrmem_create(&xdrs, request, size, XDR_ENCODE);

    struct header tohdr, outhdr;
    gf_init_header(&tohdr);
    tohdr.reqcode = BATCH_HOST_INFO;

    if (!gf_xdr_message(&xdrs, nodereq, &tohdr, &auth, gf_xdr_request, NULL)) {

        // fail to encode request.
        log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "gf_xdr_message");
        xdr_destroy(&xdrs);
        FREEUP(request);
        gf_xdr_free(gf_xdr_auth, (void *)&auth);
        quickpool_errno = ERROR_XDR;

        return NULL;
    }

    char *strMaster;
    unsigned int port;
    unsigned int connTimeout;
    unsigned int recvTimeout;
    if (NULL != master) { // client set master, let's call it directly.
        strMaster = master->master;
        port = master->port;
        connTimeout = master->conntimeout;
        recvTimeout = master->recvtimeout;
    } else {
        strMaster = qp_getmastername(); // call JM to get the maste name.
        if (NULL == strMaster) {
            // JM do not know who is master, let's try g_masterlist.
            strMaster = g_masterlist;
        }

        port = g_js_port;
        connTimeout = g_conntimeout;
        recvTimeout = g_recvtimeout;
    }

    char *reply;
    int cc, fd;
    cc = gf_callmaster(strMaster, port, connTimeout, recvTimeout, g_connretries,
                       XDR_GETPOS(&xdrs), request, &outhdr, &reply, &fd);

    if (0 < fd) {
        gf_chan_close(fd);
    }
    xdr_destroy(&xdrs);
    FREEUP(request);
    gf_xdr_free(gf_xdr_auth, (void *)&auth);

    if (0 > cc) { // communication in trouble.

        FREEUP(reply);
        log_message(ERR, NOCLASS, STR_FUNC_S_FAIL, __func__, "gf_callmaster", strMaster);
        return NULL;
    }

    struct QuickPoolHostReply *hostReply;
    hostReply = (struct QuickPoolHostReply *)calloc(1, sizeof(struct QuickPoolHostReply));
    if (NULL == hostReply) {

        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                    "calloc", sizeof(struct QuickPoolHostReply));
        FREEUP(reply);

        return NULL;
    }

    quickpool_errno = outhdr.reqcode;
    xdrmem_create(&xdrs, reply, outhdr.length, XDR_DECODE);
    if (quickpool_errno == 0) {

        // Got node list reply from JS.
        unsigned int num = 0;
        if(!libxdr_host_reply(&xdrs, hostReply, &outhdr, &num)) {

            log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "libxdr_host_reply");
            xdr_destroy(&xdrs);
            FREEUP(hostReply);
            FREEUP(reply);

            return NULL;
        }
    } else {

        // JS replied error message, decode it.
        char *strErrMsg = hostReply->error_message;        
        if (!gf_xdr_string(&xdrs, &strErrMsg, &outhdr, NULL)) {

            log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "gf_xdr_string");
            xdr_destroy(&xdrs);
            FREEUP(hostReply);
            FREEUP(reply);

            return NULL;
        }
    }

    xdr_destroy(&xdrs);
    FREEUP(reply);

    return hostReply;
} // end function client_get_hostinfo

char *
qp_getmyhostname(void)
{
    char hostname[MAXHOSTNAMELEN+1];
    gethostname(hostname, MAXHOSTNAMELEN);

    struct hostent *hostEnt;
    hostEnt = gf_gethostbyname(hostname);
    if (NULL == hostEnt) {
        hostname[0] = 0;
        return NULL;
    }

    return hostEnt->h_name;
} // end function qp_getmyhostname

static char *sstrMaster;
/**
 * @brief       API: get the quickpool master name.
 *
 * @retval      not NULL     #1: name of the master
 * @retval      NULL         #2: failed
 *
 * @note        static name is set to void calling JM multiple time.
 *              Caller does not have to free the name.
 */
char *
qp_getmastername(void)
{
    if (NULL != sstrMaster) { // had got master name, do not bother JM again.
        return sstrMaster;
    }
    
    char *myname;
    if ((myname = qp_getmyhostname()) == NULL) {
        log_message(ERR, NOCLASS, "%s: cannot get my host name", __func__);
        quickpool_errno = ERROR_NO_HOST;
        return NULL;
    }
    
    struct auth auth;
    gf_set_auth(&auth, baseParams[BASE_AUTH].string_value);

    unsigned int size; // for calculating the size of the request.
    size = gf_xdrsize_auth(&auth) + HEADER_LEN;

    char *request;
    request = (char *)calloc(size, sizeof(char));
    if (NULL == request) {

        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", size);
        gf_xdr_free(gf_xdr_auth, (void *)&auth);
        return NULL;
    }

    XDR xdrs;
    xdrmem_create(&xdrs, request, size, XDR_ENCODE);

    struct header hdr;
    gf_init_header(&hdr);
    hdr.reqcode = JM_GET_MASTINFO;
    if (!gf_xdr_message(&xdrs, NULL, &hdr, &auth, NULL, NULL)) {

        // fail to encode request.
        log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "gf_xdr_message");
        xdr_destroy(&xdrs);
        FREEUP(request);
        gf_xdr_free(gf_xdr_auth, (void *)&auth);

        return NULL;
    }

    gf_set_ignorewarn(ERR); // ignore error message in gf_callslave
    char *reply;
    struct header outhdr;
    int cc, fd = -1;
    cc = gf_callslave(myname, g_jm_port, g_conntimeout, g_recvtimeout, 
                      XDR_GETPOS(&xdrs), request, &outhdr, &reply, &fd);

    if (0 < fd) {
        gf_chan_close(fd);
        fd = -1;
    }

    gf_set_ignorewarn(NOLOG);

    if (cc < 0 || 0 != outhdr.reqcode) {
        // local JM is not started, let's call master list.
        char *list = copy_string(g_masterlist);

        char *host;
        char *savedHost;
        while ((host = strtok_r(list, " ", &savedHost)) != NULL) {
            list = NULL;

            if (0 == strcmp(myname, host)) {
                // Has called my JM and failed, try next.
                continue;
            }

            cc = gf_callslave(host, g_jm_port, g_conntimeout, g_recvtimeout, 
                              XDR_GETPOS(&xdrs), request, &outhdr, &reply, &fd);
            if (0 == cc) {
                // get the master name now, let's break out
                break;
            }
        }
        FREEUP(list);

        if (cc < 0) { // all failed.

            log_message(ERR, NOCLASS, STR_FUNC_S_FAIL, __func__, "gf_callmaster", g_masterlist);
            FREEUP(reply);
            xdr_destroy(&xdrs);
            FREEUP(request);
            gf_xdr_free(gf_xdr_auth, (void *)&auth);

            return NULL;
        }
    }

    xdr_destroy(&xdrs);
    FREEUP(request);
    gf_xdr_free(gf_xdr_auth, (void *)&auth);

    quickpool_errno = outhdr.reqcode;
    if (quickpool_errno != 0 || NULL == reply) {
        // JM replied error.
        return NULL;
    }

    // Got master name reply from JM.
    xdrmem_create(&xdrs, reply, outhdr.length, XDR_DECODE);
    if (!gf_xdr_string(&xdrs, &sstrMaster, &outhdr, NULL)) {

        // fail to decode cluster string.
        log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "gf_xdr_string");
        xdr_destroy(&xdrs);
        FREEUP(reply);

        return NULL;
    }

    xdr_destroy(&xdrs);
    FREEUP(reply);

    return sstrMaster;
} // end function qp_getmastername

/**
 * @brief       API: get the quickpool cluster name.
 *
 * @retval      not NULL     #1: name of the cluster
 * @retval      NULL         #2: failed
 *
 * @note        caller should free the cluster name string.
 */
char *
qp_getclustername(void)
{
    struct auth auth;
    gf_set_auth(&auth, baseParams[BASE_AUTH].string_value);

    struct header hdr;
    gf_init_header(&hdr);
    hdr.reqcode = JM_GET_CLUSTERNAME;

    unsigned int size; // for calculating the size of the request.
    size = gf_xdrsize_auth(&auth) + HEADER_LEN;

    char *request;
    request = (char *)calloc(size, sizeof(char));
    if (NULL == request) {

        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", size);
        gf_xdr_free(gf_xdr_auth, (void *)&auth);
        quickpool_errno = ERROR_MEMORY;
        return NULL;
    }

    XDR xdrs;
    xdrmem_create(&xdrs, request, size, XDR_ENCODE);

    if (!gf_xdr_message(&xdrs, NULL, &hdr, &auth, NULL, NULL)) {

        // fail to encode request.
        log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "gf_xdr_message");
        xdr_destroy(&xdrs);
        FREEUP(request);
        gf_xdr_free(gf_xdr_auth, (void *)&auth);

        return NULL;
    }

    char *strMaster = qp_getmastername(); // call JM to get the maste name.
    if (NULL == strMaster) {
        // JM do not know who is master, let's try g_masterlist.
        strMaster = g_masterlist;
    }

    char *reply;
    struct header outhdr;
    int cc, fd = -1;
    cc = gf_callmaster(strMaster, g_jm_port, g_conntimeout, g_recvtimeout, g_connretries,
                       XDR_GETPOS(&xdrs), request, &outhdr, &reply, &fd);

    if (0 < fd) {
        gf_chan_close(fd);
    }
    xdr_destroy(&xdrs);
    FREEUP(request);
    gf_xdr_free(gf_xdr_auth, (void *)&auth);

    if (0 > cc || NULL == reply) { // communication in trouble.

        FREEUP(reply);
        log_message(ERR, NOCLASS, STR_FUNC_S_FAIL, __func__, "gf_callmaster", strMaster);
        return NULL;
    }

    quickpool_errno = outhdr.reqcode;
    if (quickpool_errno != 0 || NULL == reply) {
        // JM replied error.
        return NULL;
    }

    // Got cluster name reply from JM.
    char *cluster = NULL;    
    xdrmem_create(&xdrs, reply, outhdr.length, XDR_DECODE);
    if (!gf_xdr_string(&xdrs, &cluster, &outhdr, NULL)) {

        // fail to decode cluster string.
        log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "gf_xdr_string");
        xdr_destroy(&xdrs);
        FREEUP(reply);

        return NULL;
    }

    xdr_destroy(&xdrs);
    FREEUP(reply);

    return cluster;
} // end function qp_getclustername

/**
 * @brief       API: get cluster information from the cluster.
 *
 * @param[in]   clusterreq   #1: query cluster request key values
 * @param[in]   master       #2: master(port and name) to be called
 *
 * @retval      not NULL     #1: succeed, or JM reply error
 * @retval      NULL         #2: failed
 *
 * @note        When succeed, QuickPoolClusterReply should be freed by the caller.
 *              When failed, error message is set to reply in this API.
 */
struct QuickPoolClusterReply *
qp_clusterinfo(struct request *clusterreq, struct poolMaster *master)
{
    if (NULL == clusterreq || 0 == clusterreq->number) {
        log_message(ERR, NOCLASS, "%s: Must specify cluster request.", __func__);
        return NULL;
    }

    // check the first key to avoid unknown request from client.
    if (REQUEST_KEY_OPTIONS != clusterreq->keyvalues[0].key) {
        log_message(ERR, NOCLASS, "%s: First key must be option for cluster info request.", __func__);
        return NULL;
    }

    int options = gf_atoi(clusterreq->keyvalues[0].value, INFINIT_INT, -1);
    if (INFINIT_INT == options) {
        log_message(ERR, NOCLASS, "%s: Option must be an integer equal to or larger than 0.",
                    __func__);
        return NULL;
    }

    struct auth auth;
    gf_set_auth(&auth, baseParams[BASE_AUTH].string_value);

    unsigned int size; // for calculating the size of the request.
    size = gf_xdrsize_request(clusterreq);
    size += gf_xdrsize_auth(&auth);
    size += HEADER_LEN;

    char *request;
    request = (char *)calloc(size, sizeof(char));
    if (NULL == request) {

        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", size);
        gf_xdr_free(gf_xdr_auth, (void *)&auth);
        quickpool_errno = ERROR_MEMORY;
	return NULL;
    }

    XDR xdrs;
    xdrmem_create(&xdrs, request, size, XDR_ENCODE);

    struct header tohdr, outhdr;
    gf_init_header(&tohdr);
    tohdr.reqcode = JM_GET_CLUSINFO;

    if (!gf_xdr_message(&xdrs, clusterreq, &tohdr, &auth, gf_xdr_request, NULL)) {

        // fail to encode request.
        log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "gf_xdr_message");
        xdr_destroy(&xdrs);
        FREEUP(request);
        gf_xdr_free(gf_xdr_auth, (void *)&auth);
        quickpool_errno = ERROR_XDR;

        return NULL;
    }

    char *strMaster;
    unsigned int port=g_jm_port;
    unsigned int connTimeout=g_conntimeout;
    unsigned int recvTimeout=g_recvtimeout;
    if (options & FROM_JS) { // JS query JM to get cluster information.
        char *strMyName;
        strMyName = qp_getmyhostname(); // get my self name.
        if (NULL == strMyName) {

            log_message(ERR, NOCLASS, "%s: cannot get my host name", __func__);
            xdr_destroy(&xdrs);
            FREEUP(request);
            gf_xdr_free(gf_xdr_auth, (void *)&auth);
            quickpool_errno = ERROR_NO_HOST;

            return NULL;
        }

        strMaster = strMyName;
    } else {
        if (NULL != master) {  // client set master, let's call it directly.
            strMaster = master->master;
            port = master->port;
            connTimeout = master->conntimeout;
            recvTimeout = master->recvtimeout;
        } else {
            strMaster = qp_getmastername();
            if (NULL == strMaster) {
                // JM do not know who is master, let's try g_masterlist.
                strMaster = g_masterlist;
            }
        }
    }

    int cc, fd;
    char *reply;
    cc = gf_callmaster(strMaster, port, connTimeout, recvTimeout, g_connretries,
                       XDR_GETPOS(&xdrs), request, &outhdr, &reply, &fd);

    if (0 < fd) {
        gf_chan_close(fd);
    }
    xdr_destroy(&xdrs);
    FREEUP(request);
    gf_xdr_free(gf_xdr_auth, (void *)&auth);

    if (0 > cc || NULL == reply) { // communication in trouble.
        FREEUP(reply);
        log_message(ERR, NOCLASS, STR_FUNC_S_FAIL, __func__, "gf_callmaster", strMaster);
        return NULL;
    }

    struct QuickPoolClusterReply *clusterInfoReply;
    clusterInfoReply = (struct QuickPoolClusterReply *)calloc(1, sizeof(struct QuickPoolClusterReply));
    if (NULL == clusterInfoReply) {

        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                    "calloc", sizeof(struct QuickPoolClusterReply));
        FREEUP(reply);

        return NULL;
    }

    quickpool_errno = outhdr.reqcode;
    if (quickpool_errno == 0) {

        // Got cluster info reply from JM.
        xdrmem_create(&xdrs, reply, outhdr.length, XDR_DECODE);
        if (!libxdr_clusterinfo_reply(&xdrs, clusterInfoReply, &outhdr, NULL)) {

            log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "libxdr_clusterinfo_reply");
            xdr_destroy(&xdrs);
            FREEUP(clusterInfoReply);
            FREEUP(reply);

            return NULL;
        }
    } else {
        // JM replied error message, decode it.
        xdrmem_create(&xdrs, reply, outhdr.length, XDR_DECODE);

        char *strErrMsg;
        strErrMsg = clusterInfoReply->error_message;        
        if (!gf_xdr_string(&xdrs, &strErrMsg, &outhdr, NULL)) {

            log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "gf_xdr_string(error_message)");
            xdr_destroy(&xdrs);
            FREEUP(clusterInfoReply);
            FREEUP(reply);

            return NULL;
        }
    }

    xdr_destroy(&xdrs);
    FREEUP(reply);

    return (clusterInfoReply);
} // end function qp_clusterinfo

int
qp_isclustername(char *name)
{
    char* clname;

    clname = qp_getclustername();

    if (NULL != clname && 0 == strcmp(clname, name)) {

        FREEUP(clname);
        return (TRUE);
    }
    FREEUP(clname);

    return FALSE;
} // end function qp_isclustername

/**
 * @brief       API: get basic quickpool information from the cluster.
 *
 * @param[in]   master       #1: master(port and name) to be called
 *
 * @retval      not NULL     #1: succeed
 * @retval      NULL         #2: failed
 *
 * @note        caller do not need to free gptrMyInfo.
 *
 */
struct quickpoolInfo *
qp_info(struct poolMaster *master)
{
    if (NULL != gptrMyInfo) {
        return gptrMyInfo;
    } else {

        gptrMyInfo = (struct quickpoolInfo *)calloc(1, sizeof(struct quickpoolInfo));
        if (NULL == gptrMyInfo) {
            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                        "calloc", sizeof(struct quickpoolInfo));
            quickpool_errno = ERROR_MEMORY;
            return NULL;
        }
    }

    struct auth auth;
    gf_set_auth(&auth, baseParams[BASE_AUTH].string_value);

    unsigned int size; // for calculating the size of the request.
    size = gf_xdrsize_auth(&auth) + HEADER_LEN;

    char *request;
    request = (char *)calloc(size, sizeof(char));
    if (NULL == request) {

        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", size);
        gf_xdr_free(gf_xdr_auth, (void *)&auth);
        quickpool_errno = ERROR_MEMORY;
        return NULL;
    }

    XDR xdrs;
    xdrmem_create(&xdrs, request, size, XDR_ENCODE);

    struct header tohdr, outhdr;
    gf_init_header(&tohdr);
    tohdr.reqcode = JM_GET_INFO;

    if (!gf_xdr_message(&xdrs, NULL, &tohdr, &auth, NULL, NULL)) {

        // fail to encode request.
        log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "gf_xdr_message");
        xdr_destroy(&xdrs);
        FREEUP(request);
        gf_xdr_free(gf_xdr_auth, (void *)&auth);
        quickpool_errno = ERROR_XDR;

        return NULL;
    }

    char *strMaster;
    unsigned int port;
    unsigned int connTimeout;
    unsigned int recvTimeout;
    if (NULL != master) { // client set master, let's call it directly.
        strMaster = master->master;
        port = master->port;
        connTimeout = master->conntimeout;
        recvTimeout = master->recvtimeout;
    } else {
        strMaster = qp_getmastername();
        if (NULL == strMaster) {
             // JM do not know who is master, let's try g_masterlist.
            strMaster = g_masterlist;
        }

        port = g_jm_port;
        connTimeout = g_conntimeout;
        recvTimeout = g_recvtimeout;
    }

    char *reply;
    int cc, fd;
    cc = gf_callmaster(strMaster, port, connTimeout, recvTimeout, g_connretries,
                       XDR_GETPOS(&xdrs), request, &outhdr, &reply, &fd);

    if (0 < fd) {
        gf_chan_close(fd);
    }
    xdr_destroy(&xdrs);
    FREEUP(request);
    gf_xdr_free(gf_xdr_auth, (void *)&auth);

    if (0 > cc || NULL == reply) { // communication in trouble.

        FREEUP(reply);
        log_message(ERR, NOCLASS, STR_FUNC_S_FAIL, __func__, "gf_callmaster", strMaster);
        return NULL;
    }

    quickpool_errno = outhdr.reqcode;
    if (quickpool_errno != 0) {

        // JM replied error message, decode it.
        xdrmem_create(&xdrs, reply, outhdr.length, XDR_DECODE);

        char *strErrMsg = NULL;
        if (!gf_xdr_string(&xdrs, &strErrMsg, &outhdr, NULL)) {

            log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "gf_xdr_string(error_message)");
            xdr_destroy(&xdrs);
            FREEUP(reply);
            return NULL;
        }

        log_message(ERR, NOCLASS, "%s failed due to %s.", __func__, strErrMsg);
        xdr_destroy(&xdrs);
        FREEUP(reply);
        FREEUP(strErrMsg);

        return NULL;
    }

    // Got quickpool basic info from JM.
    xdrmem_create(&xdrs, reply, outhdr.length, XDR_DECODE);
    if (!libxdr_quickpoolinfo(&xdrs, gptrMyInfo, &outhdr, NULL)) {

        log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "libxdr_quickpoolinfo");
        xdr_destroy(&xdrs);
        FREEUP(reply);
        return NULL;
    }

    xdr_destroy(&xdrs);
    FREEUP(reply);

    return gptrMyInfo;
} // end function qp_info

/**
 * @brief       API: call JM to get node information from the cluster.
 *
 * @param[in]   nodereq      #1: query node request key values
 * @param[in]   master       #2: master(port and name) to be called
 *
 * @retval      not NULL     #1: succeed, or JM reply error
 * @retval      NULL         #2: failed
 *
 * @note        When succeed, QuickPoolHostReply should be freed by the caller.
 *              When failed, error message is set to reply in this API.
 */
struct QuickPoolHostReply *
qp_gethostinfo(struct request *nodereq, struct poolMaster *master)
{
    if (NULL == nodereq || 0 == nodereq->number) {
        log_message(ERR, NOCLASS, "%s: Must specify node request.", __func__);
	return NULL;
    }

    // check the first key to avoid unknown request from client.
    if (REQUEST_KEY_OPTIONS != nodereq->keyvalues[0].key) {
        log_message(ERR, NOCLASS, "%s: First key must be option for node request.",
                    __func__);
	return NULL;
    }

    int options = gf_atoi(nodereq->keyvalues[0].value, INFINIT_INT, -1);
    if (INFINIT_INT == options) {
        log_message(ERR, NOCLASS, "%s: Option must be an integer equal to or larger than 0.",
                    __func__);
        return NULL;
    }

    char *strMyName;
    strMyName = qp_getmyhostname();
    if (NULL == strMyName) {
        log_message(ERR, NOCLASS, "%s: cannot get my name.", __func__);
        quickpool_errno = ERROR_NO_HOST;
        return NULL;
    }

    struct auth auth;
    gf_set_auth(&auth, baseParams[BASE_AUTH].string_value);

    unsigned int size; // for calculating the size of the request.
    size = gf_xdrsize_request(nodereq);
    size += gf_xdrsize_auth(&auth);
    size += HEADER_LEN;

    char *request;
    request = (char *)calloc(size, sizeof(char));
    if (NULL == request) {

        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", size);
        gf_xdr_free(gf_xdr_auth, (void *)&auth);
        quickpool_errno = ERROR_MEMORY;
	return NULL;
    }

    XDR xdrs;
    xdrmem_create(&xdrs, request, size, XDR_ENCODE);

    struct header hdr, outhdr;
    gf_init_header(&hdr);
    hdr.reqcode = JM_GET_HOSTINFO;

    if (!gf_xdr_message(&xdrs, nodereq, &hdr, &auth, gf_xdr_request, NULL)) {

        // fail to encode request.
        log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "gf_xdr_message");
        xdr_destroy(&xdrs);
        FREEUP(request);
        gf_xdr_free(gf_xdr_auth, (void *)&auth);
        quickpool_errno = ERROR_XDR;

        return NULL;
    }

    quickpool_errno = 0;

    char *strMaster;
    unsigned int port;
    unsigned int connTimeout;
    unsigned int recvTimeout;
    if (NULL != master) { // client set master, let's call it directly.
        strMaster = master->master;
        port = master->port;
        connTimeout = master->conntimeout;
        recvTimeout = master->recvtimeout;
    } else {
        if (options & LOCAL_ONLY) { /* js call jm */
            strMaster = strMyName;
        } else {
            strMaster = qp_getmastername();
            if (NULL == strMaster) {
                // JM do not know who is master, let's try g_masterlist.
                strMaster = g_masterlist;
            }
        }

        port = g_jm_port;
        connTimeout = g_conntimeout;
        recvTimeout = g_recvtimeout;
    }

    char *reply;
    int cc, fd=-1;
    cc = gf_callmaster(strMaster, port, connTimeout, recvTimeout, g_connretries,
                       XDR_GETPOS(&xdrs), request, &outhdr, &reply, &fd);

    if (0 < fd) {
        gf_chan_close(fd);
    }
    xdr_destroy(&xdrs);
    FREEUP(request);
    gf_xdr_free(gf_xdr_auth, (void *)&auth);

    if (0 > cc) { // communication in trouble.

        if (-2 == cc) { // JM is down
            quickpool_errno = ERROR_DAEMON_DOWN;
        }

        log_message(ERR, NOCLASS, STR_FUNC_S_FAIL, __func__, "gf_callmaster", strMaster);
        FREEUP(reply);

        return NULL;
    }

    struct QuickPoolHostReply *hostReply;
    hostReply = (struct QuickPoolHostReply *)calloc(1, sizeof(struct QuickPoolHostReply));
    if (NULL == hostReply) {

        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                    "calloc", sizeof(struct QuickPoolHostReply));
        FREEUP(reply);

        return NULL;
    }

    quickpool_errno = outhdr.reqcode;

    xdrmem_create(&xdrs, reply, outhdr.length, XDR_DECODE);
    if (quickpool_errno == 0) {

        // Got node info reply from JM.
        unsigned int num;
        if (!libxdr_host_reply(&xdrs, hostReply, &outhdr, &num)) {

            log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "libxdr_host_reply");
            xdr_destroy(&xdrs);
            FREEUP(hostReply);
            FREEUP(reply);

            return NULL;
        }
    } else {

        // JM replied error message, decode it.
        char *strErrMsg = hostReply->error_message;
        if (!gf_xdr_string(&xdrs, &strErrMsg, &outhdr, NULL)) {

            log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "gf_xdr_string");
            xdr_destroy(&xdrs);
            FREEUP(hostReply);
            FREEUP(reply);
            quickpool_errno = ERROR_XDR;

            return NULL;
        }
    }

    xdr_destroy(&xdrs);
    FREEUP(reply);

    return (hostReply);
} // end function qp_gethostinfo

/**
 * @brief       API: call JM to get node load from the cluster.
 *
 * @param[in]   loadreq      #1: query node load request key values
 * @param[in]   master       #2: master(port and name) to be called
 *
 * @retval      not NULL     #1: succeed, or JM reply error
 * @retval      NULL         #2: failed
 *
 * @note        When succeed, QuickPoolHostLoadReply should be freed by the caller.
 *              When failed, error message is set to reply in this API.
 */
struct QuickPoolHostLoadReply *
qp_gethostload(struct request *loadreq, struct poolMaster *master)
{
    if (NULL == loadreq || 0 == loadreq->number) {
        log_message(ERR, NOCLASS, "%s: Must specify node load request.", __func__);
	return NULL;
    }

    // check the first key to avoid unknown request from client.
    if (REQUEST_KEY_OPTIONS != loadreq->keyvalues[0].key) {
        log_message(ERR, NOCLASS, "%s: First key must be option for node load request.",
                    __func__);
	return NULL;
    }

    int options = gf_atoi(loadreq->keyvalues[0].value, INFINIT_INT, -1);
    if (INFINIT_INT == options) {
        log_message(ERR, NOCLASS, "%s: Option must be an integer equal to or larger than 0.",
                    __func__);
        return NULL;
    }

    char *strMyName;
    strMyName = qp_getmyhostname();
    if (NULL == strMyName) {
        log_message(ERR, NOCLASS, "%s: cannot get my name.", __func__);
        quickpool_errno = ERROR_NO_HOST;
       return NULL;
    }

    quickpool_errno = 0;

    struct auth auth;
    gf_set_auth(&auth, baseParams[BASE_AUTH].string_value);

    unsigned int size; // for calculating the size of the request.
    size = gf_xdrsize_request(loadreq);
    size += gf_xdrsize_auth(&auth);
    size += HEADER_LEN;

    char *request;
    request = (char *)calloc(size, sizeof(char));
    if (NULL == request) {

        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", size);
        gf_xdr_free(gf_xdr_auth, (void *)&auth);
        quickpool_errno = ERROR_MEMORY;
	return NULL;
    }

    XDR xdrs;
    xdrmem_create(&xdrs, request, size, XDR_ENCODE);

    struct header hdr, outhdr;
    gf_init_header(&hdr);
    hdr.reqcode = JM_LOAD_REQ;

    if (!gf_xdr_message(&xdrs, loadreq, &hdr, &auth, gf_xdr_request, NULL)) {

        // fail to encode request.
        log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "gf_xdr_message");
        xdr_destroy(&xdrs);
        FREEUP(request);
        gf_xdr_free(gf_xdr_auth, (void *)&auth);
        quickpool_errno = ERROR_XDR;

        return NULL;
    }

    char *strMaster;
    unsigned int port;
    unsigned int connTimeout;
    unsigned int recvTimeout;
    if (NULL != master) { // client set master, let's call it directly.
        strMaster = master->master;
        port = master->port;
        connTimeout = master->conntimeout;
        recvTimeout = master->recvtimeout;
    } else {
        if (options & LOCAL_ONLY) { /* js call jm */
            strMaster = strMyName;
        } else {
            strMaster = qp_getmastername();
            if (NULL == strMaster) {
                // JM do not know who is master, let's try g_masterlist.
                strMaster = g_masterlist;
            }
        } 

        port = g_jm_port;
        connTimeout = g_conntimeout;
        recvTimeout = g_recvtimeout;
    }

    char *reply = NULL;
    int cc, fd;
    cc = gf_callmaster(strMaster, port, connTimeout, recvTimeout, g_connretries,
                       XDR_GETPOS(&xdrs), request, &outhdr, &reply, &fd);

    if (0 < fd) {
        gf_chan_close(fd);
    }
    xdr_destroy(&xdrs);
    FREEUP(request);
    gf_xdr_free(gf_xdr_auth, (void *)&auth);

    if (0 > cc) { // communication in trouble.

        if (-2 == cc) { // JM is down 
            quickpool_errno = ERROR_DAEMON_DOWN;
        }

        FREEUP(reply);
        log_message(ERR, NOCLASS, STR_FUNC_S_FAIL, __func__, "gf_callmaster", strMaster);

        return NULL;
    }

    struct QuickPoolHostLoadReply *loadReply;
    loadReply = (struct QuickPoolHostLoadReply *)calloc(1, sizeof(struct QuickPoolHostLoadReply));
    if (NULL == loadReply) {

        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                    "calloc", sizeof(struct QuickPoolHostReply));
        FREEUP(reply);

        return NULL;
    }

    quickpool_errno = outhdr.reqcode;
    if (quickpool_errno != 0) {
        // JM replied error message, decode it.
        xdrmem_create(&xdrs, reply, outhdr.length, XDR_DECODE);

        char *strErrMsg = loadReply->error_message;
        if (!gf_xdr_string(&xdrs, &strErrMsg, &outhdr, NULL)) {

            log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "gf_xdr_string(error_message)");
            xdr_destroy(&xdrs);
            FREEUP(loadReply);
            FREEUP(reply);

            return NULL;
        }

        return (loadReply);
    }

    // Got node load reply from JM.
    xdrmem_create(&xdrs, reply, outhdr.length, XDR_DECODE);

    if (!libxdr_load_reply(&xdrs, loadReply, &outhdr, NULL)) {

        log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "libxdr_load_reply");
        xdr_destroy(&xdrs);
        FREEUP(loadReply);
        FREEUP(reply);

        return NULL;
    }

    xdr_destroy(&xdrs);
    FREEUP(reply);

    return loadReply;
} // end function qp_gethostload

/**
 * @brief       API: add node to the cluster.
 *
 * @param[in]   req          #1: add node request key values
 * @param[in]   master       #2: master(port and name) to be called
 *
 * @retval      0            #1: succeed
 * @retval      -1           #2: failed
 *
 * @note        When failed, error message is printed out in this API.
 */
int
client_addhost(struct request *req, struct poolMaster *master)
{
    if (NULL == req) {
        log_message(ERR, NOCLASS, "%s: wrong input parameters(null request)", __func__);
        return -1;
    }

    // check the first key to avoid unknown request from client.
    if (0 == req->number || REQUEST_KEY_HOSTNAME != req->keyvalues[0].key) {
        log_message(ERR, NOCLASS, "%s: did not specify a node name.", __func__);
    }

    struct auth auth;
    gf_set_auth(&auth, commandParams[AUTH_KIND].string_value);

    unsigned int size; // for calculating the size of the request.
    size = gf_xdrsize_request(req);
    size += gf_xdrsize_auth(&auth);
    size += HEADER_LEN;

    char *request;
    request = (char *)calloc(size, sizeof(char));
    if (NULL == request) {

        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", size);
        gf_xdr_free(gf_xdr_auth, (void *)&auth);
        quickpool_errno = ERROR_MEMORY;
	return -1;
    }

    XDR xdrs;
    xdrmem_create(&xdrs, request, size, XDR_ENCODE);

    struct header tohdr, outhdr;
    gf_init_header(&tohdr);
    tohdr.reqcode = JM_ADD_HOST;

    if (!gf_xdr_message(&xdrs, req, &tohdr, &auth, gf_xdr_request, NULL)) {

        // fail to encode request.
        log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "gf_xdr_message");
        xdr_destroy(&xdrs);
        FREEUP(request);
        gf_xdr_free(gf_xdr_auth, (void *)&auth);
        quickpool_errno = ERROR_XDR;

        return -1;
    }

    char *strMaster;
    unsigned int port;
    unsigned int connTimeout;
    unsigned int recvTimeout;    
    if (NULL != master) { // client set master, let's call it directly.
        strMaster = master->master;
        port = master->port;
        connTimeout = master->conntimeout;
        recvTimeout = master->recvtimeout;
    } else {
        strMaster = qp_getmastername();
        if (NULL == strMaster) {
             // JM do not know who is master, let's try g_masterlist.
            strMaster = g_masterlist;
        }

        port = g_jm_port;
        connTimeout = g_conntimeout;
        recvTimeout = g_recvtimeout;
    }

    char *reply;
    int cc, fd;
    cc = gf_callmaster(strMaster, port, connTimeout, recvTimeout, g_connretries,
                       XDR_GETPOS(&xdrs), request, &outhdr, &reply, &fd);

    if (0 < fd) {
        gf_chan_close(fd);
    }
    xdr_destroy(&xdrs);
    FREEUP(request);
    gf_xdr_free(gf_xdr_auth, (void *)&auth);

    if (0 > cc) { // communication in trouble.

        FREEUP(reply);
        log_message(ERR, NOCLASS, STR_FUNC_S_FAIL, __func__, "gf_callmaster", strMaster);
        return -1;
    }

    quickpool_errno = outhdr.reqcode;
    if (quickpool_errno != 0) {

        // JM replied error message, decode it.
        xdrmem_create(&xdrs, reply, outhdr.length, XDR_DECODE);

        char *strErrMsg = NULL;
        if (!gf_xdr_string(&xdrs, &strErrMsg, &outhdr, NULL)) {

            log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "gf_xdr_string");
            xdr_destroy(&xdrs);
            FREEUP(reply);
            return -1;
        }

        if (NULL != strErrMsg) {
            log_message(ERR, NOCLASS, "%s", strErrMsg);
        } else {
            log_message(ERR, NOCLASS, "%s: unknown error occurs.", __func__);
        }

        FREEUP(strErrMsg);
        xdr_destroy(&xdrs);
        FREEUP(reply);

        return -1;
    }

    FREEUP(reply);
    
    // add node succeed.
    return 0;
} // end function client_addhost

/**
 * @brief       API: remove node from the cluster.
 *
 * @param[in]   host         #1: name of the node being removed
 * @param[in]   master       #2: master(port and name) to be called
 *
 * @retval      0            #1: succeed
 * @retval      -1           #2: failed
 *
 * @note        When failed, error message is printed out in this API.
 */
int
client_removehost(const char *host, struct poolMaster *master)
{
    if (NULL == host) {
        log_message(ERR, NOCLASS, "%s: wrong input parameters(null host)", __func__);
        return -1;
    }

    struct auth auth;
    gf_set_auth(&auth, commandParams[AUTH_KIND].string_value);

    unsigned int size; // for calculating the size of the request.
    size = strlen(host) + 1 + sizeof(int) + XDR_OFFSET;
    size += gf_xdrsize_auth(&auth);
    size += HEADER_LEN;

    char *request;
    request = (char *)calloc(size, sizeof(char));
    if (NULL == request) {

        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", size);
        gf_xdr_free(gf_xdr_auth, (void *)&auth);
        quickpool_errno = ERROR_MEMORY;
	return -1;
    }

    XDR xdrs;
    xdrmem_create(&xdrs, request, size, XDR_ENCODE);

    struct header tohdr, outhdr;
    gf_init_header(&tohdr);
    tohdr.reqcode = JM_RM_HOST;

    if (!gf_xdr_message(&xdrs, (void*)&host, &tohdr, &auth, gf_xdr_string, NULL)) {

        // fail to encode request.
        log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "gf_xdr_message(host)");
        xdr_destroy(&xdrs);
        FREEUP(request);
        gf_xdr_free(gf_xdr_auth, (void *)&auth);
        quickpool_errno = ERROR_XDR;

        return -1;
    }

    char *strMaster;
    unsigned int port;
    unsigned int connTimeout;
    unsigned int recvTimeout;    
    if (NULL != master) { // client set master, let's call it directly.
        strMaster = master->master;
        port = master->port;
        connTimeout = master->conntimeout;
        recvTimeout = master->recvtimeout;
    } else {
        strMaster = qp_getmastername();
        if (NULL == strMaster) {
             // JM do not know who is master, let's try g_masterlist.
            strMaster = g_masterlist;
        }

        port = g_jm_port;
        connTimeout = g_conntimeout;
        recvTimeout = g_recvtimeout;
    }

    char *reply;
    int cc, fd;
    cc = gf_callmaster(strMaster, port, connTimeout, recvTimeout, g_connretries,
                       XDR_GETPOS(&xdrs), request, &outhdr, &reply, &fd);

    if (0 < fd) {
        gf_chan_close(fd);
    }
    xdr_destroy(&xdrs);
    FREEUP(request);
    gf_xdr_free(gf_xdr_auth, (void *)&auth);

    if (cc < 0) { // communication in trouble.

        FREEUP(reply);
        log_message(ERR, NOCLASS, STR_FUNC_S_FAIL, __func__, "gf_callmaster", strMaster);
        return -1;
    }

    quickpool_errno = outhdr.reqcode;
    if (quickpool_errno != 0) {

        // JM replied error message, decode it.
        xdrmem_create(&xdrs, reply, outhdr.length, XDR_DECODE);

        char *strErrMsg = NULL;
        if (!gf_xdr_string(&xdrs, &strErrMsg, &outhdr, NULL)) {

            log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "gf_xdr_string");
            xdr_destroy(&xdrs);
            FREEUP(reply);
            return -1;
        }

        if (NULL != strErrMsg) {
            log_message(ERR, NOCLASS, "%s", strErrMsg);
        } else  {
            log_message(ERR, NOCLASS, "%s: unknown error occurs.", __func__);
        }

        FREEUP(strErrMsg);
        xdr_destroy(&xdrs);
        FREEUP(reply);

        return -1;
    }

    FREEUP(reply);

    // remove node succeed.
    return 0;
} // end function client_removehost

/**
 * @brief       read dynamic node file and store dynamic nodes request in a list.
 *
 * @retval      0             #1: succeed, no dynamic.nodes file
 * @retval      1             #2: succeed
 * @retval      -1            #3: failed, open file failure.
 *
 * @note        Remove request from the list if node has been removed.
 */
int 
load_dynamic_node(LIST_T *dynnodes)
{
    char strDynNodeFile[PATH_MAX+1];
    snprintf(strDynNodeFile, PATH_MAX, "%s/dynamic.nodes", baseParams[BASE_WORKDIR].string_value);
    if (access(strDynNodeFile, F_OK) < 0) {
        // there is no dynamic node file means the cluster has no dynamic node.       
        return 0;
    }

    FILE *fileHandle;
    fileHandle = fopen(strDynNodeFile, "r");
    if (NULL == fileHandle) { // failed to open dynamic node file.
        log_message(ERR, NOCLASS, "%s: fopen(%s) failed due to %m", __func__, strDynNodeFile);
        return -1;
    } 

    quickpool_errno = 0;   

    unsigned int lineNum = 0;
    while (quickpool_errno != ERROR_EOF) {
        struct record *nodeRec;

        nodeRec = read_file_line(fileHandle, &lineNum);
        if (NULL == nodeRec) {

            if (quickpool_errno != ERROR_EOF) {
                log_message(DEBUG, TRACER, "%s: failed to read dynamic.nodes.", __func__);
                continue;
            }

            break;
        }

        struct request *req;
        req = (struct request *)calloc(1, sizeof(struct request));

        req->number = nodeRec->numKVs;
        req->keyvalues = (key_value_t *)calloc(req->number, sizeof(key_value_t));

        char *strNode=NULL;
        unsigned int i;
        for (i = 0; i < nodeRec->numKVs; i++) {
            switch (nodeRec->KVs[i].key) {
            case RECORD_KEY_HOST_NAME:
                req->keyvalues[i].key = REQUEST_KEY_HOSTNAME;
                req->keyvalues[i].value = copy_string(nodeRec->KVs[i].value);
                strNode = req->keyvalues[i].value;
                break;
            case RECORD_KEY_HOST_MODEL:
                req->keyvalues[i].key = REQUEST_KEY_HOST_MODEL;
                req->keyvalues[i].value = copy_string(nodeRec->KVs[i].value);
                break;
            case RECORD_KEY_HOST_TYPE:
                req->keyvalues[i].key = REQUEST_KEY_HOST_TYPE;
                req->keyvalues[i].value = copy_string(nodeRec->KVs[i].value);
                break;
            case RECORD_KEY_RUN_WINDOW:
                req->keyvalues[i].key = REQUEST_KEY_RUN_WINDOW;
                req->keyvalues[i].value = copy_string(nodeRec->KVs[i].value);
                break;
            case RECORD_KEY_DISPATCH_WINDOW:
                req->keyvalues[i].key = REQUEST_KEY_DISPATCH_WINDOW;
                req->keyvalues[i].value = copy_string(nodeRec->KVs[i].value);
                break;
            case RECORD_KEY_RESOURCE:
                req->keyvalues[i].key = REQUEST_KEY_RESOURCE;
                req->keyvalues[i].value = copy_string(nodeRec->KVs[i].value);
            case RECORD_KEY_MAX_PROCESSOR:
                req->keyvalues[i].key = REQUEST_KEY_MAXJOB;
                req->keyvalues[i].value = copy_string(nodeRec->KVs[i].value);
                break;
            default:
                log_message(ERR, NOCLASS, "%s: unknown key <%d>", __func__, nodeRec->KVs[i].key);
                break;
            }
        }

        struct dynamicNode *dynamicNode;
        if (RECORD_TYPE_ADDNODE == nodeRec->type) {

            dynamicNode = (struct dynamicNode *)calloc(1, sizeof(struct dynamicNode));
            if (NULL == dynamicNode) {
                log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc",
                            sizeof(struct dynamicNode));
                fclose(fileHandle);
                return -1;
            }

            dynamicNode->node_req = req;

            gf_list_insert(dynnodes, dynnodes, (LIST_T *)dynamicNode);

            continue;
        } else if (RECORD_TYPE_DELNODE == nodeRec->type) {

            dynamicNode = (struct dynamicNode *)gf_list_search(dynnodes, strNode, compare_node_name);
            if (NULL != dynamicNode) {

                gf_list_remove(dynnodes, (LIST_T *)dynamicNode);
                free_dynamic_node(dynamicNode);
                dynamicNode = NULL;
            }
        }
    }

    fclose(fileHandle);
    return 1;
} // end function load_dynamic_node

void
free_dynamic_node(void *dynamicnode)
{
    struct dynamicNode *dynNode = (struct dynamicNode *)dynamicnode;

    gf_xdr_free(gf_xdr_request, dynNode->node_req);
    FREEUP(dynNode->node_req);
    FREEUP(dynNode);

    return;
}

/* ------------------------------------------------
 *
 *  below are static functions used in this file
 *
 * ------------------------------------------------ */

static int
compare_node_name(void *nodeent, void *nodename)
{
    struct dynamicNode *dynNode = (struct dynamicNode *)nodeent;
    char *strNodeName = (char *)nodename;

    struct request *nodeReq = dynNode->node_req;

    bool_t compared = FALSE;
    unsigned int i;
    for (i = 0; i < nodeReq->number; i++) {
        switch (nodeReq->keyvalues[i].key) {
        case REQUEST_KEY_HOSTNAME:

            compared = TRUE;
            if (0 == strcmp(nodeReq->keyvalues[i].value, strNodeName)) {
                return TRUE;
            }

            break;
        default:
            break;
        }

        if (compared) {
            break;
        }
    }

    return FALSE;
} // end function compare_host_name
